a tool for shared writing and social publishing
1import { z } from "zod";
2import { makeRoute } from "../lib";
3import type { Env } from "./route";
4import { AtUri } from "@atproto/syntax";
5import { getFactsFromHomeLeaflets } from "./getFactsFromHomeLeaflets";
6import { normalizeDocumentRecord } from "src/utils/normalizeRecords";
7import { ids } from "lexicons/api/lexicons";
8
9export type GetPublicationDataReturnType = Awaited<
10 ReturnType<(typeof get_publication_data)["handler"]>
11>;
12export const get_publication_data = makeRoute({
13 route: "get_publication_data",
14 input: z.object({
15 did: z.string(),
16 publication_name: z.string(),
17 }),
18 handler: async (
19 { did, publication_name },
20 { supabase }: Pick<Env, "supabase">,
21 ) => {
22 let pubLeafletUri;
23 let siteStandardUri;
24 if (/^(?!\.$|\.\.S)[A-Za-z0-9._:~-]{1,512}$/.test(publication_name)) {
25 pubLeafletUri = AtUri.make(
26 did,
27 ids.PubLeafletPublication,
28 publication_name,
29 ).toString();
30 siteStandardUri = AtUri.make(
31 did,
32 ids.SiteStandardPublication,
33 publication_name,
34 ).toString();
35 }
36 let { data: publication, error } = await supabase
37 .from("publications")
38 .select(
39 `*,
40 documents_in_publications(documents(
41 *,
42 comments_on_documents(count),
43 document_mentions_in_bsky(count),
44 recommends_on_documents(count)
45 )),
46 publication_subscriptions(*, identities(bsky_profiles(*))),
47 publication_domains(*),
48 leaflets_in_publications(*,
49 documents(*),
50 permission_tokens(*,
51 permission_token_rights(*),
52 custom_domain_routes!custom_domain_routes_edit_permission_token_fkey(*)
53 )
54 )`,
55 )
56 .or(
57 `name.eq."${publication_name}", uri.eq."${pubLeafletUri}", uri.eq."${siteStandardUri}"`,
58 )
59 .eq("identity_did", did)
60 .order("uri", { ascending: false })
61 .limit(1)
62 .single();
63
64 let leaflet_data = await getFactsFromHomeLeaflets.handler(
65 {
66 tokens:
67 publication?.leaflets_in_publications.map(
68 (l) => l.permission_tokens?.root_entity!,
69 ) || [],
70 },
71 { supabase },
72 );
73
74 // Pre-normalize documents from documents_in_publications
75 const documents = (publication?.documents_in_publications || [])
76 .map((dip) => {
77 if (!dip.documents) return null;
78 const normalized = normalizeDocumentRecord(
79 dip.documents.data,
80 dip.documents.uri,
81 );
82 if (!normalized) return null;
83 return {
84 uri: dip.documents.uri,
85 record: normalized,
86 indexed_at: dip.documents.indexed_at,
87 sort_date: dip.documents.sort_date,
88 data: dip.documents.data,
89 commentsCount: dip.documents.comments_on_documents[0]?.count || 0,
90 mentionsCount: dip.documents.document_mentions_in_bsky[0]?.count || 0,
91 recommendsCount:
92 dip.documents.recommends_on_documents?.[0]?.count || 0,
93 };
94 })
95 .filter((d): d is NonNullable<typeof d> => d !== null);
96
97 // Pre-filter drafts (leaflets without published documents, not archived)
98 const drafts = (publication?.leaflets_in_publications || [])
99 .filter((l) => !l.documents)
100 .filter((l) => !(l as { archived?: boolean }).archived)
101 .map((l) => ({
102 leaflet: l.leaflet,
103 title: l.title,
104 permission_tokens: l.permission_tokens,
105 // Keep the full leaflet data for LeafletList compatibility
106 _raw: l,
107 }));
108
109 return {
110 result: {
111 publication,
112 documents,
113 drafts,
114 leaflet_data: leaflet_data.result,
115 },
116 };
117 },
118});